#!/usr/bin/env python
#coding=utf-8

"""
Implementación concurrente en lenguaje Python del problema del Barbero Durmiente. 
Vea http://es.wikipedia.org/wiki/Problema_del_barbero_durmiente

Copyright © 2009 Martín Gaitán  http://nqnwebs.com

Este programa es software libre: usted puede redistribuirlo y/o modificarlo conforme a los términos de la Licencia Pública General de GNU publicada por
la Fundación para el Software Libre, ya sea la versión 3 de esta Licencia o (a su elección) cualquier versión posterior.

Este programa se distribuye con el deseo de que le resulte útil, pero SIN GARANTÍAS DE NINGÚN TIPO; ni siquiera con las garantías implícitas de COMERCIABILIDAD o APTITUD PARA UN PROPÓSITO DETERMINADO.  Para más información, consulte la Licencia Pública General de GNU.

Junto con este programa, se debería incluir una copia de la Licencia Pública General de GNU.
De no ser así, ingrese en <http://www.gnu.org/licenses/>.
"""

"""
Para ejecutar este programa, compile todos los fuentes y ejecute BarberoDurmiente
python BarberoDurmiente.py
"""





from threading import *
from random import random
import time


#usa lumpy para generar diagrama de objetos y clases

#http://www.greenteapress.com/thinkpython/swampy/lumpy.html

#from Lumpy import Lumpy
#lumpy = Lumpy()
#lumpy.make_reference()



        
class Barbero(Thread):    
    def __init__(self, salon, id=''):
        Thread.__init__(self)
        self.s = salon
        self.id = id

    def run(self):
        while 1:
            self.quiereCortar()

    def quiereCortar(self):
        print "El Barbero %s está libre, esperando por un cliente" % self.id
        self.s.cliente.acquire()   #esperando cliente
        self.s.mutex.acquire()     #exclusion mutua liberada
        self.s.esperando = self.s.esperando - 1 #decrementa la cola de clientes
        self.s.barbero.release()              #barbero se duerme
    
        #si llegó acá es porque un cliente lo despertó
        print "El Barbero %s sentó a un cliente en su silla  (Sillas ocupadas: %i/%i)" % (self.id, self.s.esperando, self.s.num_sillas)    
        self.s.mutex.release()                    #el sillon está ocupado
        print "El Barbero %s está cortando cabello"  % self.id     
        self.s.cortar.acquire()                   #cortando cabello

        

class Cliente(Thread):

    def __init__(self, salon, id):
        Thread.__init__(self)
        self.s = salon
        self.id = id

    def run(self):
        while 1:
            print "Al cliente %i le está creciendo el cabello" % self.id
            time.sleep(random() * 20.0)  #tiempo aleatorio en que le crece el pelo
            print "El cliente %i necesita un corte de cabello" % self.id
            self.quiereCorte()


    def quiereCorte(self):
        self.s.mutex.acquire()                    #intento entrar
        if (self.s.esperando < self.s.num_sillas):       #hay lugar?
            self.s.esperando = self.s.esperando + 1      #si hay lugar me quedo esperando en una silla   
            print "El Cliente %i está esperando en la sala.  (Sillas ocupadas: %i/%i)" % (self.id, self.s.esperando, self.s.num_sillas)
            self.s.cliente.release()                  #hay un cliente mas!
            self.s.mutex.release()                    #a por la silla del barbero!

            #si llegó hasta acá es porque el barbero le dio turno
            self.s.barbero.acquire()                  
            print "El cliente %i está recibiendo un corte de cabello" % self.id
            time.sleep(random() * 15.0)     #tiempo que tarda el barbero en peluquearlo
            self.s.cortar.release()           #terminó el corte!
        else:
            #no hubo lugar!
            print "Cliente %i se retira, la sala está llena" % self.id
            self.s.mutex.release()


class Salon():   
    def __init__(self, num_sillas):
        self.num_sillas = num_sillas
        self.esperando = 0           #num de clientes esperando
        self.cliente = Semaphore(0)  #sem para clientes
        self.barbero = Semaphore(0)  #sem para el barbero ocupado/no ocupado
        self.cortar = Semaphore(0)   #señala cuando terminó el corte
        self.mutex = Semaphore(1)    #exclusion mutua. 
       
         

            

if __name__ == "__main__":
    """inicializo
       1 salon con 4 sillas
       1 barbero
       10 clientes
    """
    
    num_sillas = 4;
    num_clientes = 10;
    s = Salon(num_sillas);
    Barbero(s, "A").start();
    Barbero(s, "B").start();
    for i in range(num_clientes):
        Cliente(s,i).start();
    
    
    #lumpy.object_diagram()
    #lumpy.class_diagram()
    
    
    
